home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
EnigmA Amiga Run 1995 October
/
EnigmA AMIGA RUN 01 (1995)(G.R. Edizioni)(IT)[!][issue 1995-10][Aminet 7].iso
/
Aminet
/
dev
/
asm
/
Asm_Course2.lha
/
Teil16.TXT
< prev
next >
Wrap
Text File
|
1993-09-01
|
11KB
|
317 lines
A S S E M B L E R - K U R S (c) Jeff Kandle 1990
16.Teil...
So, nachdem wir wissen wie wir unsere sachen auch weitergeben und damit
Strunzen koennen, macht es doch gleich wieder spass, oder ?
In diesem Teil werde ich am anfang erstmal ein paar neue Befehle
vorstellen, sie aber nicht in irgendein programm verpacken. Ich bin sicher
das ihr diese Befehle irgendwann gut gebrauchen koennt, und dann erinnert
ihr euch bestimmt daran.
Als ersten nette befehl kommt der Swap-befehl. Er kann auf die Adress und
Datenregister zugreifen, und vertauscht oberes Wort mit dem unteren.
Aus...
$40981546
wird...
$15464098
Naja, hoert sich ja nicht besonders an. Aber den grund warum ich den Befehl
vorstelle, ich hatte ja gesagt ich stelle nur die Befehle vor die man
wirklich zum Intro schreiben braucht, ist, das er sich ganz besonders gut
fuer manipulationen an Copperlisten eignet, wenn es darum geht Adressen
einzutragen.
Wie ihr wisst sind die Adresse in einer Copperliste immer getrennt in
Highwort und Lowwort.
Um die adresse $40000 als adresse fuer die erste Bitplane festzulegen, muss
man das ja die beiden befehle..
$00e0 $0004
$00e2 $0000
schreiben. Naja wenn wir wissen wo das liegt koennen wir das ja relativ
einfach eintragen. Aber was ist wenn wir zum Beispiel die Adresse der
Sprite datenliste in die Copperliste eintragen wollen. Dann gibt es schon
schwierigkeiten. Wir koennten das zwar ermitteln, weil wir ja das mainprog
mit Org/Load fixieren. Aber die Adresse der Datenliste aendert sich ja
jedesmal wenn wir erweiterungen am listing vornehmen, also das ist es nicht.
Die moeglichkeit ist also, sich mit Move.l # die Adresse in ein Datenregister
zu holen, um dann die Zwei worte in die Copperliste einzutragen.
In der Praxis sieht das dann so aus...
Move.l #Sprite1,d0
Move.w d0,Copperlist+2
Swap d0
Move.w d0,Copperlist+6
Rts
Copperlist:
dc.w $0120,$0000
dc.w $0122,$0000
dc.l $-2
Sprite1:
..Hier liegt dann die Spritedefinition
Die Funktion muesste eigentlich Klar sein...
Dann kommt ein Kuerzel das man z.b hinter die Befehle Move, Add und Sub
klemmen kann.
Naemlich `q`...
Es bedeutet dann soviel wie Quick, man kann damit allerdings nur
vorzeichenbehaftete 4 bit werte uebertragen. Also von -8 bis +7.
Quick wird es genannt weil es sehr schnell geht, denn der wert wird direkt
in den Befehl mit eingebaut. Es kommt nur ein Wort bei raus, und das der
Prozessor das Schneller abarbeiten kann ist ja wohl jedem Klar..ne.
Wenn irgendwie moeglich solltet ihr wenn irgendwie moeglich bei Sprung
operationen immer die Branch befehle vor ziehen. Also anstatt
Jmp und Jsr...immer Bra und Bsr...
Denn dort ist sowieso der Befehl kuerzer. Man kann ihn aber auch von hand
noch kuerzer machen. Indem man einfach .S fuer kurze Distanz, oder .L fuer
lange Distanz eingebt. Wenn ihr euch vertut und Kurz statt lang eingebt ist
das nicht so schlimm, der Seka merkt das und verbessert euch. Falls aber
umgekehrt, wenn ihr also .L schreibt, aber .S moeglich waere, tja dann habt
ihr pech gehabt, da mischt sich der Seka nicht ein.
Dieses Kuerzel habt ihr aber auch schon in meinen Listings gesehen. Es
klebt immer an den Bne`s, Beq`s, und an den Bsr`s dran. Naja jetzt wisst
ihr auch was das bedeutet.
Eine weiteres Kuerzel kann man noch in den Daten fuer den befehl
unterbringen. Und zwar wenn man mit Adresse arbeitet die eigentlich nur ein
Wort lang sind, dann waere es unsinnig ein ganzes langwort dafuer zu
verschwenden. Es koennte dann so aussehen...
Vorher
Move.l $00000040,$00000050
schiebt das langwort von $40 nach $50
Das ergibt nach dem Assemblieren
2 Bytes fuer den Move befehl
4 Bytes fuer die Adresse $00000040
4 Bytes fuer die Adresse $00000050
----------------------------------
10 Bytes
Wenn ihr aber die Kuerzel benutzt, sieht es so aus.
Move.l $0040.w,$0050.w
belegt..
2 Bytes fuer den Move befehl
2 Bytes fuer die Adresse $00000040
2 Bytes fuer die Adresse $00000050
----------------------------------
6 Bytes
Denkt daran, die schreibweise $0040 aendert nichts, das .w muss dahinter,
denn ich kann den Befehl mit den Langwoertern auch so schreiben.
Move.l $40,$50
Es bleiben langwoerter, und als solche werden sie eingesetzt.
Naja, wir haben also im Beispiel 4 Bytes gespart, was das ist nicht viel,
das meint ihr aber auch nur, wenn ihr mal euren ersten Bootblock
beschreibt, dann werdet ihr noch an mich denken, den dort zaehlt jedes
Byte.
Leider gibt es kein Kuerzel .B, denn dann waere die ersparnis noch
groesser.
Jetzt kommt ein Befehl den ich manchmal benutze, um schnell irgend was
auszutauschen.
Stellt euch vor ihr habt irgendwann mal eine tolle Routine geschrieben, die
einen Haufen sachen ausrechnet, und die ergebnisse in allen Datenregistern,
das heisst von 0 bis 7. Und irgendwann schreibt ihr mal ein programm das
halt solche werte braucht, und ihr erinnert euch an die alte
Routine...Jetzt merkt ihr aber das die routine etwas anders ist, und zwar
so das das Ergebnis was die neue Routinen in d6 braucht, in d7 liegt, und
das ergebnis was sie in d7 braucht, aber in d6 liegt.
Jetzt gibt es drei moeglichkeiten dieser Situation Herr zu werden.
Wir schreiben eine der beiden Routinen um...was aber bei 20 Kilobyte
SourceCode nicht gerade leicht ist. Und da Programmierer grundsaetzlich
faule Menschen sind, faellt das also weg.
Also muessen wir gucken wie wir da sachen vertauscht kriegen, so viel ist
klar. Man kann es mit dem Stack, oder mit einer X-beliebigen adresse
machen. Hier die zwei moeglichkeiten.
Move.l d6,$40000
Move.l d7,d6
Move.l $40000,d7
Oder
Move.l d6,-(a7)
Move.l d7,d6
Move.l (a7)+,d7
Die zweite moeglichkeit ist etwas komplizierter, aber wird im Source
kuerzer als die erste. Der effekt ist derselbe, was vorher in d6 stand
steht jetzt in d7, und umgekehrt.
Aber jetzt tritt der neue Befehl auf die Buehne. Er heisst schlicht und
ergreifend `Exchange` kurz `EXG`, und mit ihm passiert dann die ganze
sachen in einem Wort.
Exg d6,d7
Das wars...Im prinzip ist es dasselbe wie die kuerzere Variante von eben,
denn der Prozessor macht genau das auch. Nur wenn er es macht geht es
natuerlich viel schneller. Alles Klor ?
Es kommt immer mal wieder vor das man, wenn man ein Intro programmiert, die
einzelnen Effekte in Modulen abarbeiten laesst. Dabei kann es vorkommen das
man die module an verschiedenen Tagen programmiert, und nicht darauf achtet
welche Arbeits und datenregister man den nun noch frei hat. Und wenn man
ein Intro mit 20 Effektmodulen hat, dann wird es eng mit den registern, mit
denen man doch so hervorragend arbeiten kann, oder.
So, da gibt es eine ganz einfache sache. Wir legen uns jeden die inhalte der
Datenregister und der Adressregister jedes Modules, einen Sicherungsplatz
an, indem wir nach abarbeitung des Modul alle sachen abspeichern, und bevor
wir das naechste mal mit dem modul anfangen, holen wir uns einfach die
sachen wieder rein. Das saehe, wenn wir es jetzt machen muessten allerdings
etwas lang, naemlich so aus.
Move.l d0,$40000
Move.l d1,$40004
....
....
Move.l a0,$40020
Move.l a1,$40024
naja das waere ja ein bisschen lang. Das haben sich die Leute die den 68000
gebaut haben auch gedacht, und deshalb fuer den move befehl noch ein Kurzel
ausgedacht. naemlich `m`. Das `m` steht fuer viele oder mehrere, das heisst
wir koennen mit einem Befehl direkt mehrere register retten. Wir koennen
sie dann auf den Stack oder einfach in den Speicher schreiben. Der Befehl
der den Kompletten registerinhalt nach $40000 rettet sehae dann so aus.
Movem.l d0-d7/a0-a6,$40000
Er erhoeht auch selbsttaetig die adresse wo er das hinschreibt.
Wir muessen allerdings nicht alles retten, sondern wir koennen auch ganz
bestimmt retten. Wenn wir nur d0,d1,d4,d5,d7,a0,a1,a2 und a6 retten wollen
schreiben wir einfach
Movem.l d0-d1/d4-d5/d7/a0-a2/a6,$40000
Also ihr seht..ziemlich cool der Befehl. Natuerlich geht es zurueck
genauso. Der Befehl der den ersten Movem befehl von mir rueckgaengig macht
heisst
Movem.l $40000,d0-d7/a0-a6
Mehr nicht...
Nach moeglichkeit den Clr befehl in langen schleifen und auch sonst meiden.
Denn er dauert einfach zu lange. Ein Ersatz fuer diese Schleife
Lea $40000,a0
Move.w #$ffff,d0
Clearloop:
Clr.l (a0)+
Dbf d0,clearloop
Rts
waere ohne Clr.l, und sehr viel schneller...
Lea $40000,a0
Move.l #$ffff,d0
Clearloop:
Moveq.l #0,(a0)+
Dbf d0,clearloop
Rts
All right ?
Wenn ihr mal etwas groessere sachen programmiert, und ihr kleine Zeitliche
abstaende braucht, dann koennt ihr euch ja mit der einfachen Warteschleife
helfen die ihr schon kennt. Aber gerade bei sachen in Echtzeit, muss man
manchmal kurze abstaende warten. Je nach dem sind die Abstaende sogar so
kurz, das man noch nicht mal eine kleine Routine dazwischensetzen kann.
Dazu gibt es einen netten Befehl, der eigentlich nichts macht. Und so
heisst er auch..
`Nop` - No OPeration
Der Prozessor braucht Vier Taktzyklen um zu merken das der Befehl garkeinen
gueltigen Code ergibt, dann springt er weiter. Solche Codes gibt es noch
viele andere. Die Erbauer von Prozessoren, nehmen immer einen davon der
dann den Namen `Nop` kriegt, und dann ist das so.
So jetzt kommt die Aufstellung der Sprungkriterien fuer die Befehle aus der
Gruppe der Bedingten Branch Befehle. Zwei davon kennt ihr ja schon, Bne und
Beq, ich zaehle sie aber der komplettheit wegen nochmal auf.
Das B davor muesst ihr euch immer denken, da ich nur die bedingung
beschreibe.
CC = Carry Flag nicht gesetzt (Carry Flag wird spaeter erklaert)
CS = Carry Flag gesetzt
EQ = Zero Flag gesetzt, oder letzte Operation gleich 0
GE = Groesser oder Gleich > =
GT = Groesser, aber mit vorzeichen >
HI = Groesser >
LE = Kleiner oder 0, aber mit vorzeichen <
LS = Kleiner oder Gleich < =
LT = Kleiner, aber mit vorzeichen <
MI = Wert im Minus Bereich -
NE = Zero Flag nicht gesetzt, oder letzte operation nicht 0 <>
PL = Wert im Plus Bereich
VC = Overflow nicht gesetzt, mit vorzeichen (Overflow Flag kommt spaeter)
VS = Overflow gesetzt, mit vorzeichen
So das waeren sie, den Groessten teil braucht man halt sehr selten, es ist
aber schon mal gut wenn man weiss das es sie ueberhaupt gibt.
So jetzt noch ein Paar Klaerende worte zum Carry- und Overflow Flag.
Zuerst da carryflag....
Was die Flags sind wisst ihr ja schon, deshalb kann ich ja loslegen.
Das Carry Flag ist praktisch das 33 ste Bit der Datenregister, wenn ein
Wert durch Addition zu gross fuer ein Datenregister wird, dann wird der
rest ins Carry geschrieben. Deshalb mus man es bei Addtion und Subtraktion
immer beruecksichtigen.
Das Overflowflag ist immer dann gesetzt wenn bei der letzten operation, ein
bereich ueberschritten wurde, sei es der Byte, Wort oder Langwort bereich.
Sobald die Zahl diesen Ueberlaueft wird es gesetzt. Wofuer das gut sein
soll weiss ich zwar nicht, aber ich habe es so gelernt.
So jetzt erstmal in die Heia, muss morgen inne Schule...
bis denne
Jeff Kandle